package com.qf.aspect;

import com.qf.annotation.MyLog;
import com.qf.entity.SysUser;
import com.qf.entity.SysLog;
import com.qf.mapper.SysLogMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;


/**
 * @ClassName: LogAspect
 * @Author: WangHao
 * @Date: 2025/4/30 10:44
 * @Description:
 * @Version: 1.0
 */

@Aspect
@Component
public class LogAspect {

    @Autowired
    private SysLogMapper logMapper;

    /**
     * 增强方法,用于记录日志
     * 此处使用后置通知 @After,即方法执行后才记录日志
     * 里面写切入的目标路径,此处比较特殊
     * 目标是日志注解 @Log,所以里面是 @annotation(com.qf.annotation.Log)
     * ----------------------------
     * JoinPoint joinPoint: 切入点对象(即被拦截的方法对象),可以获得方法名,参数等信息
     * ----------------------------
     * 这个recordLog方法什么时候执行的? 是在加了@Log注解的方法执行之后执行
     */
    @After("@annotation(com.qf.annotation.MyLog)")
    public void recordLog(JoinPoint joinPoint){

        // 获得当前请求对象
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        // ip
        String ip = getIpAddress(request);

        // method
        String method = request.getMethod( );
        // 获得session,从session中获取用户信息
        SysUser sysUser = (SysUser) request.getSession( ).getAttribute( "sysUser" );
        String name = "未登录";
        if (sysUser!= null) {
            name = sysUser.getUsername();
        }

        // 解析日志注解
        HashMap<String, String> map = getAnnoLog(joinPoint);
        // 插入数据库
        SysLog sysLog = new SysLog( );
        sysLog.setLogTime(new Date(  ));
        sysLog.setIp(ip);
        sysLog.setMethod(method);
        sysLog.setName(name);
        sysLog.setLog(map.get("log"));
        sysLog.setType(map.get("type"));
        logMapper.insertLog(sysLog);
    }

    /**
     * 获得访问者ip地址
     */
    private String getIpAddress(HttpServletRequest request) {
        String ip = request.getHeader("X-Forwarded-For");
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        return ip;
    }

    /**
     * 封装的方法,通过反射技术获得目标方法上的注解的值
     */
    public static HashMap<String, String> getAnnoLog(JoinPoint joinPoint) {
        HashMap<String, String> map = new HashMap<>( );
        try {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature( );
            // 获得目标方法对象
            Method method = signature.getMethod( );
            // 获得目标方法对象上的日志注解
            MyLog myLog = method.getDeclaredAnnotation(MyLog.class);
            // 获得日志注解上的值
            String log = myLog.value( );
            String type = myLog.type( );

            map.put("log", log);
            map.put("type", type);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return map;
    }

}